﻿using System;
using System.Drawing;
using System.Text;

using Storm.TextEditor.Parser.Objects;

namespace Storm.TextEditor.Editor.Exporters
{
    /// <summary>
    /// Represents an exporter that exports text of a document to an Html webpage.
    /// </summary>
    public class HtmlExporter
    {
        #region Fields

        private StringBuilder stringBuilder = null;

        #endregion

        #region Methods

        #region Public

        /// <summary>
        /// Exports the content of a SyntaxDocument to a HTML formatted string.
        /// </summary>
        /// <param name="document">SyntaxDocument object to export from.</param>
        /// <param name="imagePath">File path to the images to use in the HTML string.</param>
        /// <returns>The text of the given SyntaxDocument as HTML source code.</returns>
        public string Export(SyntaxDocument document, string imagePath)
        {
            return Export(document, Color.Transparent, imagePath, "");
        }

        /// <summary>
        /// Exports the content of a SyntaxDocument to a HTML formatted string.
        /// </summary>
        /// <param name="document">SyntaxDocument object to export from.</param>
        /// <param name="backColor">HTML color string to use as background color.</param>
        /// <param name="imagePath">File path to the images to use in the HTML string.</param>
        /// <param name="style">HTML style string that should be applied to the output.</param>
        /// <returns>The text of the given SyntaxDocument as HTML source code.</returns>
        public string Export(SyntaxDocument document, Color backColor, string imagePath, string style)
        {
            stringBuilder = new StringBuilder();
            document.ParseAll(true);

            int index = 0;
            string guid = DateTime.Now.Ticks.ToString();

            if (backColor.A == 0)
                this.Out("<table><tr><td nowrap><div style=\"" + style + "\">");
            else
                this.Out("<table style=\"background-color:" + this.GetHtmlColor(backColor)
                    + ";" + style + "\"><tr><td nowrap><div>");

            foreach (Row row in document)
            {
                index++;
                if (row.CanFold == true)
                {
                    this.RenderCollapsed(row.VirtualCollapsedRow, row, index, imagePath, guid);
                    this.Out("<div style=\"display:block;\" id=\"open" + guid + "_"
                        + index.ToString() + "\">");

                    string minusImagePath = "minus.gif";
                    try
                    {
                        if (row.ExpansionStartSegment.Parent.Parent == null)
                            minusImagePath = "minusNoTopLine.gif";
                    }
                    catch
                    {
                    }

                    this.Out("<img src=\"" + imagePath + minusImagePath + "\" align=top onclick=\"open" + guid + "_" + index.ToString() +
                        ".style.display='none'; closed" + guid + "_" + index.ToString() + ".style.display='block'; \">");
                }
                else
                {
                    if (row.CanFoldEndPart == true)
                        this.Out("<img src=\"" + imagePath + "L.gif\"  align=top>");
                    else
                    {
                        if (row.HasExpansionLine == true)
                            this.Out("<img src=\"" + imagePath + "I.gif\"  align=top>");
                        else
                            this.Out("<img src=\"" + imagePath + "clear.gif\"  align=top>");
                    }
                }

                foreach (Word word in row)
                    this.Write(word.Text, word.Style);

                if (row.CanFoldEndPart == true)
                    this.Out("</div>\n");
                else
                    this.Out("<br>\n");
            }

            this.Out("</div></td></tr></table>");
            return stringBuilder.ToString();
        }

        #endregion

        #region Private

        /// <summary>
        /// Renders the given Row as if it were collapsed.
        /// </summary>
        /// <param name="row">Row to render as collapsed.</param>
        /// <param name="trueRow">The true Row.</param>
        /// <param name="index">Index of the Row.</param>
        /// <param name="imagePath">Path to the image to use for the rendering.</param>
        /// <param name="guid">GUID.</param>
        private void RenderCollapsed(Row row, Row trueRow, int index, string imagePath, string guid)
        {
            this.Out("<div style=\"display:none;\" id=\"closed" + guid + "_" + index.ToString() + "\">");

            string plusImagePath = "plus.gif";
            try
            {
                if (trueRow.ExpansionStartSegment.Parent.Parent == null)
                    plusImagePath = "PlusNoLines.gif";
            }
            catch
            {
            }

            this.Out("<img src=\"" + imagePath + plusImagePath + "\" align=top onclick=\"open" + guid + "_" + index.ToString() +
                ".style.display='block'; closed" + guid + "_" + index.ToString() + ".style.display='none'; \">");

            foreach (Word word in row)
                this.Write(word.Text, word.Style);

            this.Out("</div>");
        }

        /// <summary>
        /// Writes the given text to the StringBuilder using the given TextStyle.
        /// </summary>
        /// <param name="text">Text to write.</param>
        /// <param name="textStyle">TextStyle to use.</param>
        private void Write(string text, TextStyle textStyle)
        {
            if (textStyle != null)
            {
                if (textStyle.Bold == true)
                    this.Out("<b>");

                if (textStyle.Italic == true)
                    this.Out("<i>");

                if (textStyle.Transparent == true)
                    this.Out("<span style=\"color:" + this.GetHtmlColor(textStyle.ForeColor) + "\">");
                else
                {
                    this.Out("<span style=\"color:" + this.GetHtmlColor(textStyle.ForeColor) + ";background-color:" +
                        this.GetHtmlColor(textStyle.BackColor) + ";\">");
                }
            }

            text = text.Replace("&", "&amp;");
            text = text.Replace("<", "&lt;");
            text = text.Replace(">", "&gt;");
            text = text.Replace(" ", "&nbsp;");
            text = text.Replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
            this.Out(text);

            if (textStyle != null)
            {
                this.Out("</span>");
                if (textStyle.Italic == true)
                    this.Out("</i>");

                if (textStyle.Bold == true)
                    this.Out("</b>");
            }
        }

        /// <summary>
        /// Returns the given color as an Html color.
        /// </summary>
        /// <param name="color">Color to convert.</param>
        /// <returns>The given color as an Html color.</returns>
        private string GetHtmlColor(Color color)
        {
            return string.Format("#{0}{1}{2}", color.R.ToString("x2"), color.G.ToString("x2"), color.B.ToString("x2"));
        }

        /// <summary>
        /// Appends the given text to the StringBuilder of the HtmlExporter.
        /// </summary>
        /// <param name="text">Text to append to the StringBuilder.</param>
        private void Out(string text)
        {
            stringBuilder.Append(text);
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of HtmlExporter.
        /// </summary>
        public HtmlExporter()
        {
        }
    }
}
